package com.cskaoyan.wordmemorize.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cskaoyan.wordmemorize.common.constant.CommonConstant;
import com.cskaoyan.wordmemorize.common.constant.ResultCodeEnum;
import com.cskaoyan.wordmemorize.common.constant.UserVocBoundEnum;
import com.cskaoyan.wordmemorize.common.exception.BusinessException;
import com.cskaoyan.wordmemorize.converter.ReviewConverter;
import com.cskaoyan.wordmemorize.dao.entity.*;
import com.cskaoyan.wordmemorize.dao.mapper.*;
import com.cskaoyan.wordmemorize.dto.admin.PageDTO;
import com.cskaoyan.wordmemorize.dto.app.UserCheckinDTO;
import com.cskaoyan.wordmemorize.dto.app.UserCheckinStatisticsDTO;
import com.cskaoyan.wordmemorize.request.PageRequest;
import com.cskaoyan.wordmemorize.request.UserCheckinCommand;
import com.cskaoyan.wordmemorize.service.UserCheckinService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

@Service
public class UserCheckinServiceImpl implements UserCheckinService {

    @Autowired
    UserCheckinMapper userCheckinMapper;

    @Autowired
    UserCheckinStatisticsMapper userCheckinStatisticsMapper;

    @Autowired
    UserMapper userMapper;

    @Autowired
    UserVocBoundLogMapper userVocBoundLogMapper;

    @Autowired
    UserVocBoundStatisticsMapper userVocBoundStatisticsMapper;

    @Autowired
    LevelMapper levelMapper;
    @Autowired
    UserVocRevMapper userVocRevMapper;

    @Autowired
    ReviewConverter reviewConverter;

    @Override
    public PageDTO<UserCheckinDTO> getCheckInData(PageRequest pageRequest, Long userId) {
        Page<UserCheckinDO> pageParam = new Page<>(pageRequest.getPageNum(), pageRequest.getCount());
        LambdaQueryWrapper<UserCheckinDO> userCheckinWrapper = new LambdaQueryWrapper<>();
        userCheckinWrapper.eq(UserCheckinDO::getUserId, userId);
        Page<UserCheckinDO> userCheckinDOPage = userCheckinMapper.selectPage(pageParam, userCheckinWrapper);
        return reviewConverter.userCheckinPage2PageDTO(userCheckinDOPage.getRecords(), userCheckinDOPage.getTotal());
    }

    @Transactional
    @Override
    public UserCheckinStatisticsDTO checkin(UserCheckinCommand command) {
        UserCheckinDO userCheckinDO = reviewConverter.userCheckinCommand2DO(command);

        // 判断打卡日期是否重复
        LambdaQueryWrapper<UserCheckinDO> eq = new LambdaQueryWrapper<UserCheckinDO>()
                .eq(UserCheckinDO::getUserId, userCheckinDO.getUserId())
                .eq(UserCheckinDO::getSignDate, userCheckinDO.getSignDate());
        UserCheckinDO userCheckinDO1 = userCheckinMapper.selectOne(eq);
        if (userCheckinDO1 != null) {
            throw new BusinessException(ResultCodeEnum.CHECK_IN_DATE_DUPLICATE);
        }
        LocalDate today = LocalDate.now();
        LocalDate yesterday = today.minusDays(1);

        // 判断今日单词是否复习完成
        List<UserVocRevDO> userVocRevDO = userVocRevMapper.selectList(new LambdaQueryWrapper<UserVocRevDO>()
                .eq(UserVocRevDO::getUserId, userCheckinDO.getUserId())
                .eq(UserVocRevDO::getType, 1)
                .eq(UserVocRevDO::getNextReviewDate,today));
        if (!CollectionUtils.isEmpty(userVocRevDO)){
            throw new BusinessException(ResultCodeEnum.OLD_WORD_NOT_REVIEW);
        }
        List<UserVocRevDO> userVocRevDO1 = userVocRevMapper.selectList(new LambdaQueryWrapper<UserVocRevDO>()
                .eq(UserVocRevDO::getUserId, userCheckinDO.getUserId())
                .eq(UserVocRevDO::getType, 0));
        if (!CollectionUtils.isEmpty(userVocRevDO1)){
            throw new BusinessException(ResultCodeEnum.NEW_WORD_NOT_RECITE);
        }
        // 计算连续签到天数
        UserCheckinStatisticsDO userCheckinStatisticsDO = userCheckinStatisticsMapper.selectOne(
                new LambdaQueryWrapper<UserCheckinStatisticsDO>()
                        .eq(UserCheckinStatisticsDO::getUserId, userCheckinDO.getUserId()));
        LocalDate curContinuousStartDate = userCheckinStatisticsDO.getCurContinuousStartDate();
        if (curContinuousStartDate != null) {
            Integer curContinuousDays = userCheckinStatisticsDO.getCurContinuousDays();
            LocalDate lastCheckinDate = curContinuousStartDate.plusDays(curContinuousDays - 1);
            if (!(today.equals(lastCheckinDate) || yesterday.equals(lastCheckinDate))) {
                userCheckinStatisticsDO.setCurContinuousDays(0);
            }
        } else {
            userCheckinStatisticsDO.setCurContinuousDays(0);
        }

        // 最近连续签到日期为0
        if (userCheckinStatisticsDO.getCurContinuousDays() == 0) {
            // 今天以前都没签到

            // 更新user_checkin表
            userCheckinMapper.insert(userCheckinDO);
            userCheckinDO.setStickNo(userCheckinDO.getId());
            userCheckinMapper.updateById(userCheckinDO);
            // 更新user_checkin_statistics表
            // 总签到天数
            userCheckinStatisticsDO.setTotalDays(userCheckinStatisticsDO.getTotalDays() + 1);
            // 当前最大连续签到天数
            userCheckinStatisticsDO.setCurContinuousDays(1);
            // 与最大连续签到天数比较，并更新
            userCheckinStatisticsDO.setCurContinuousStartDate(userCheckinDO.getSignDate());
            if (userCheckinStatisticsDO.getMaxContinuousDays() == 0) {
                userCheckinStatisticsDO.setMaxContinuousDays(1);
            }
            if (userCheckinStatisticsDO.getMaxContinuousStartDate() == null) {
                userCheckinStatisticsDO.setMaxContinuousStartDate(userCheckinDO.getSignDate());
            }
            // 更新user_checkin_statistics表
            userCheckinStatisticsMapper.updateById(userCheckinStatisticsDO);

        } else {
            // 更新user_checkin表
            LambdaQueryWrapper<UserCheckinDO> eq1 = new LambdaQueryWrapper<UserCheckinDO>()
                    .eq(UserCheckinDO::getUserId, userCheckinDO.getUserId())
                    .eq(UserCheckinDO::getSignDate, curContinuousStartDate);
            UserCheckinDO userCheckinDO2 = userCheckinMapper.selectOne(eq1);
            if (userCheckinDO2 == null){
                throw new RuntimeException("userStatistics表数据错误");
            }
            userCheckinDO.setStickNo(userCheckinDO2.getStickNo());
            userCheckinMapper.insert(userCheckinDO);

            // 更新user_checkin_statistics表
            // 总签到天数
            userCheckinStatisticsDO.setTotalDays(userCheckinStatisticsDO.getTotalDays() + 1);
            // 当前最大连续签到天数
            userCheckinStatisticsDO.setCurContinuousDays(userCheckinStatisticsDO.getCurContinuousDays() + 1);
            // 与最大连续签到天数比较，并更新
            if (userCheckinStatisticsDO.getMaxContinuousDays() < userCheckinStatisticsDO.getCurContinuousDays()) {
                userCheckinStatisticsDO.setMaxContinuousDays(userCheckinStatisticsDO.getCurContinuousDays());
                userCheckinStatisticsDO.setMaxContinuousStartDate(userCheckinStatisticsDO.getCurContinuousStartDate());
            }
            userCheckinStatisticsMapper.updateById(userCheckinStatisticsDO);
        }

        //更新 user_voc_bound_statistics

        // 计算用户当前单词量
        UserVocBoundStatisticsDO userVocBoundStatisticsDO = userVocBoundStatisticsMapper.selectOne(
                new LambdaQueryWrapper<UserVocBoundStatisticsDO>()
                        .eq(UserVocBoundStatisticsDO::getUserId, userCheckinDO.getUserId()));
        userVocBoundStatisticsDO.setTotal(userVocBoundStatisticsDO.getTotal() + 1);
        userVocBoundStatisticsDO.setAvailable(userVocBoundStatisticsDO.getAvailable() + 1);
        userVocBoundStatisticsDO.setFree(userVocBoundStatisticsDO.getFree() + 1);
        userVocBoundStatisticsMapper.updateById(userVocBoundStatisticsDO);

        // user_voc_bound_log更新
        UserVocBoundLogDO userVocBoundLogDO = UserVocBoundLogDO.builder()
                .userId(userCheckinDO.getUserId())
                .logDate(today)
                .type(UserVocBoundEnum.USER_VOC_BOUND_LOG_TYPE_CHECKIN.getValue())
                .count(CommonConstant.USER_CHECKIN_VOC_REWARD)
                .description(UserVocBoundEnum.USER_VOC_BOUND_LOG_TYPE_CHECKIN.getDescription())
                .build();
        userVocBoundLogMapper.insert(userVocBoundLogDO);

        // 更新User表
        UserDO userDO = userMapper.selectById(userCheckinDO.getUserId());
        Integer currentLevel = userDO.getLevelValue();

        LambdaQueryWrapper<LevelDO> levelDOLambdaQueryWrapper = new LambdaQueryWrapper<>();
        levelDOLambdaQueryWrapper.eq(LevelDO::getLevelValue, currentLevel + 1);
        LevelDO nextLevel = levelMapper.selectOne(levelDOLambdaQueryWrapper);

        if (nextLevel != null || currentLevel != CommonConstant.CHECK_IN_TYPE_MAX_LEVEL) {
            if (currentLevel < 5) {
                if (userCheckinStatisticsDO.getTotalDays() >= nextLevel.getCheckInDays()) {
                    userDO.setLevelName(nextLevel.getLevelName());
                    userDO.setLevelValue(currentLevel + 1);
                    userDO.setLevelDate(today);
                }
            } else if (currentLevel >= 6) {
                if (userCheckinStatisticsDO.getCurContinuousDays() >= nextLevel.getCheckInDays()) {
                    userDO.setLevelName(nextLevel.getLevelName());
                    userDO.setLevelValue(currentLevel + 1);
                    userDO.setLevelDate(today);
                }
            } else if (currentLevel == 5 && userCheckinStatisticsDO.getTotalDays() >= nextLevel.getCheckInDays()) {
                nextLevel = levelMapper.selectOne(
                        new LambdaQueryWrapper<LevelDO>()
                                .eq(LevelDO::getLevelValue, nextLevel.getLevelValue() + 1)
                );
                while (userCheckinStatisticsDO.getCurContinuousDays() >= nextLevel.getCheckInDays()) {
                    userDO.setLevelName(nextLevel.getLevelName());
                    userDO.setLevelValue(nextLevel.getLevelValue());
                    userDO.setLevelDate(LocalDate.now());
                    nextLevel = levelMapper.selectOne(
                            new LambdaQueryWrapper<LevelDO>()
                                    .eq(LevelDO::getLevelValue, nextLevel.getLevelValue() + 1)
                    );
                }
            }
            userMapper.updateById(userDO);
        }

        return reviewConverter.userCheckinStatisticsDO2DTO(userCheckinStatisticsDO);
    }

    @Override
    public UserCheckinStatisticsDTO getUserCheckinStatistics(Long userId) {
        UserCheckinStatisticsDO userCheckinStatisticsDO = userCheckinStatisticsMapper.selectOne(
                new LambdaQueryWrapper<UserCheckinStatisticsDO>()
                        .eq(UserCheckinStatisticsDO::getUserId, userId));
        if (userCheckinStatisticsDO == null) {
            return null;
        }
        Long totalTime = userCheckinStatisticsDO.getTotalTime();
        userCheckinStatisticsDO.setTotalTime(totalTime/1000/60);
        // 今天
        LocalDate today = LocalDate.now();
        // 昨天
        LocalDate yesterday = today.minusDays(1);
        // 最近连续签到起始日期
        LocalDate curContinuousStartDate = userCheckinStatisticsDO.getCurContinuousStartDate();

        if (curContinuousStartDate == null) {
            userCheckinStatisticsDO.setCurContinuousDays(0);
            userCheckinStatisticsMapper.updateById(userCheckinStatisticsDO);
            return reviewConverter.userCheckinStatisticsDO2DTO(userCheckinStatisticsDO);
        }
        // 连续签到天数
        Integer curContinuousDays = userCheckinStatisticsDO.getCurContinuousDays();
        // 最近签到日期
        LocalDate lastCheckinDate = curContinuousStartDate.plusDays(curContinuousDays - 1);

        if (!(lastCheckinDate.equals(today) || lastCheckinDate.equals(yesterday))) {
            userCheckinStatisticsDO.setCurContinuousDays(0);
            userCheckinStatisticsMapper.updateById(userCheckinStatisticsDO);
        }
        return reviewConverter.userCheckinStatisticsDO2DTO(userCheckinStatisticsDO);
    }

    @Override
    public UserCheckinStatisticsDTO getUserContinuousCheckinInfo(Long userId) {
        UserCheckinStatisticsDO userCheckinStatisticsDO = userCheckinStatisticsMapper.selectOne(
                new LambdaQueryWrapper<UserCheckinStatisticsDO>()
                        .eq(UserCheckinStatisticsDO::getUserId, userId));

        LocalDate today = LocalDate.now();
        LocalDate yesterday = today.minusDays(1);
        LocalDate curContinuousStartDate = userCheckinStatisticsDO.getCurContinuousStartDate();
        if (curContinuousStartDate != null) {
            Integer curContinuousDays = userCheckinStatisticsDO.getCurContinuousDays();
            LocalDate lastCheckinDate = curContinuousStartDate.plusDays(curContinuousDays - 1);
            if (!(today.equals(lastCheckinDate) || yesterday.equals(lastCheckinDate))) {
                userCheckinStatisticsDO.setCurContinuousDays(0);
            }
            return reviewConverter.userCheckinStatisticsDO2DTO(userCheckinStatisticsDO);
        } else {
            userCheckinStatisticsDO.setCurContinuousDays(0);
            return reviewConverter.userCheckinStatisticsDO2DTO(userCheckinStatisticsDO);
        }
    }

    @Transactional
    @Override
    public void userResignCard(Long userId) {
        // 判断是否有补签卡
        UserDO userDO1 = userMapper.selectOne(new LambdaQueryWrapper<UserDO>()
                .eq(UserDO::getId, userId));
        if (userDO1.getHasSignCard()==0){
            throw new BusinessException(ResultCodeEnum.USER_RESIGN_CARD_NOT_EXIST);
        }
        // 判断今日是否打卡
        LocalDate today = LocalDate.now();
        LambdaQueryWrapper<UserCheckinDO> eq = new LambdaQueryWrapper<UserCheckinDO>()
                .eq(UserCheckinDO::getUserId,userId)
                .eq(UserCheckinDO::getSignDate,today);
        UserCheckinDO userCheckinDO1 = userCheckinMapper.selectOne(eq);
        if (userCheckinDO1 == null) {
            throw new BusinessException(ResultCodeEnum.USER_CHECKIN_DATA_NEED_SYNC);
        }
        UserCheckinStatisticsDO userCheckinStatisticsDO = userCheckinStatisticsMapper.selectOne(
                new LambdaQueryWrapper<UserCheckinStatisticsDO>()
                        .eq(UserCheckinStatisticsDO::getUserId, userId)
        );
        // 可补签天数
        int i = userCheckinStatisticsDO.getMaxContinuousDays() - userCheckinStatisticsDO.getCurContinuousDays();
        if (i<=0) {
                throw new BusinessException(ResultCodeEnum.CHECK_IN_DATE_NOT_EXIST);
        }
        LocalDate startDate = findStartDate(userId, i, userCheckinStatisticsDO);
        int j = 0;
        // 补签
        for (j = 0;; j++) {
            // 先判断是否重复打卡
            LocalDate curDate = startDate.plusDays(j);
            LambdaQueryWrapper<UserCheckinDO> eq2 = new LambdaQueryWrapper<UserCheckinDO>()
                    .eq(UserCheckinDO::getUserId, userId)
                    .eq(UserCheckinDO::getSignDate, curDate);
            UserCheckinDO userCheckinDO2 = userCheckinMapper.selectOne(eq2);
            if (userCheckinDO2 != null) {
                break;
            }

            UserCheckinDO userCheckinDO = new UserCheckinDO();
            userCheckinDO.setUserId(userId);
            userCheckinDO.setSignDate(curDate);
            userCheckinDO.setType(0);
            if (j==0){
                userCheckinMapper.insert(userCheckinDO);
                userCheckinDO.setStickNo(userCheckinDO.getId());
                userCheckinMapper.updateById(userCheckinDO);
            }else{
                LambdaQueryWrapper<UserCheckinDO> eq1 = new LambdaQueryWrapper<UserCheckinDO>()
                        .eq(UserCheckinDO::getUserId, userCheckinDO.getUserId())
                        .eq(UserCheckinDO::getSignDate, startDate);
                UserCheckinDO userCheckinDO3 = userCheckinMapper.selectOne(eq1);
                userCheckinDO.setStickNo(userCheckinDO3.getStickNo());
                userCheckinMapper.insert(userCheckinDO);
            }
        }
        LambdaQueryWrapper<UserCheckinDO> eq1 = new LambdaQueryWrapper<UserCheckinDO>()
                .eq(UserCheckinDO::getUserId, userId)
                .eq(UserCheckinDO::getSignDate, startDate);
        // 拿到补签的stickNo
        UserCheckinDO userCheckinDO3 = userCheckinMapper.selectOne(eq1);
        Long startStickNo = userCheckinDO3.getStickNo();
        // 更新已签到的stickNo
        for (int count = 0;;count++){
            LocalDate curDate = startDate.plusDays(count);
            LambdaQueryWrapper<UserCheckinDO> eq2 = new LambdaQueryWrapper<UserCheckinDO>()
                    .eq(UserCheckinDO::getUserId, userId)
                    .eq(UserCheckinDO::getSignDate, curDate);
            UserCheckinDO userCheckinDO = userCheckinMapper.selectOne(eq2);
            if (userCheckinDO == null) {
                break;
            }
            userCheckinDO.setStickNo(startStickNo);
            userCheckinMapper.updateById(userCheckinDO);
        }
        // 更新用户连续签到天数(更新user_checkin_statistics)
        userCheckinStatisticsDO.setCurContinuousDays(userCheckinStatisticsDO.getCurContinuousDays() + j);
        userCheckinStatisticsDO.setTotalDays(userCheckinStatisticsDO.getTotalDays() + j);
        userCheckinStatisticsDO.setCurContinuousStartDate(startDate);
        userCheckinStatisticsMapper.updateById(userCheckinStatisticsDO);

        // 更新user表判断是否升级
        UserDO userDO = userMapper.selectById(userId);
        userDO.setHasSignCard(0);
        Integer currentLevel = userDO.getLevelValue();

        LambdaQueryWrapper<LevelDO> levelDOLambdaQueryWrapper = new LambdaQueryWrapper<>();
        levelDOLambdaQueryWrapper.eq(LevelDO::getLevelValue, currentLevel + 1);
        LevelDO nextLevel = levelMapper.selectOne(levelDOLambdaQueryWrapper);

        if (nextLevel != null || currentLevel != CommonConstant.CHECK_IN_TYPE_MAX_LEVEL) {
            if (currentLevel < 5) {
                if (userCheckinStatisticsDO.getTotalDays() >= nextLevel.getCheckInDays()) {
                    userDO.setLevelName(nextLevel.getLevelName());
                    userDO.setLevelValue(currentLevel + 1);
                    userDO.setLevelDate(today);
                }
            } else if (currentLevel >= 6) {
                if (userCheckinStatisticsDO.getCurContinuousDays() >= nextLevel.getCheckInDays()) {
                    userDO.setLevelName(nextLevel.getLevelName());
                    userDO.setLevelValue(currentLevel + 1);
                    userDO.setLevelDate(today);
                }
            } else if (currentLevel == 5 && userCheckinStatisticsDO.getTotalDays() >= nextLevel.getCheckInDays()) {
                nextLevel = levelMapper.selectOne(
                        new LambdaQueryWrapper<LevelDO>()
                                .eq(LevelDO::getLevelValue, nextLevel.getLevelValue() + 1)
                );
                while (userCheckinStatisticsDO.getCurContinuousDays() >= nextLevel.getCheckInDays()) {
                    userDO.setLevelName(nextLevel.getLevelName());
                    userDO.setLevelValue(nextLevel.getLevelValue());
                    userDO.setLevelDate(LocalDate.now());
                    nextLevel = levelMapper.selectOne(
                            new LambdaQueryWrapper<LevelDO>()
                                    .eq(LevelDO::getLevelValue, nextLevel.getLevelValue() + 1)
                    );
                }
            }
            userMapper.updateById(userDO);
        }

    }

    private LocalDate findStartDate(Long userId, int i, UserCheckinStatisticsDO userCheckinStatisticsDO) {
        for (; i >0; i--){
            LocalDate curContinuousStartDate = userCheckinStatisticsDO.getCurContinuousStartDate();
            // 当前补签的日期
            LocalDate curDate = curContinuousStartDate.minusDays(i);
            UserCheckinDO userCheckinDO = userCheckinMapper.selectOne(
                    new LambdaQueryWrapper<UserCheckinDO>()
                            .eq(UserCheckinDO::getUserId, userId)
                            .eq(UserCheckinDO::getSignDate, curDate)
            );
            // 找到需要补签的起始日期
            if (userCheckinDO == null) {
                return curDate;
            }
        }
        throw new BusinessException(ResultCodeEnum.CHECK_IN_DATE_NOT_EXIST);
    }
}
